/*                                    _
 *   ___ _ __ ___ _ __ ___   ___   __| |
 *  / __| '__/ __| '_ ` _ \ / _ \ / _` |
 *  \__ \ |  \__ \ | | | | | (_) | (_| |
 *  |___/_|  |___/_| |_| |_|\___/ \__,_|      
 *
 *           _  _ _. _  _ . _  _ 
 *        \/(/_| _\|(_)| ||| |(_|
 *                             _|
 *     __|_ _ _|_ _   |_  _ | _  _  _
 *    _\ | (_| | (/_  | |(/_||_)(/_| 
 *                          |                                 
 ******************************************
 ******************************************
 ******************************************
 *  _    |_ |. _   |`    _  __|_. _  _  _
 * |_)|_||_)||(_  ~|~|_|| |(_ | |(_)| |_\
 * |                                     
 *		// Ensures that all external version dependencies are satisfied
 *		Versioning_CheckDependencies()
 *
 ******************************************
 ******************************************
 ******************************************
 * . _ _  _ | _  _ _  _  _ _|_ _ _|_. _  _ 
 * || | ||_)|(/_| | |(/_| | | (_| | |(_)| |
 *       |                                 
 *		At compile-time this provides verification that we are building
 *		against left4downtown.inc. It doesn't yet verify the right version,
 *		so it would be possible to build against the older one but then 
 *		it wouldn't run anyway due to the version check.
 *
 *		Everything else is a runtime check. It checks for the version CVARs
 *		for sourcemod_version and left4downtown_version to be the required
 *		versions or newer by parsing the a.b.c.d into an integer 
 * 		roughly equivalent to the 4 bytes `a b c d` if laid out in big endian
 *		memory.
 */
 

#if !defined _l4do_included
#error "Must be built with left4downtown.inc, get it from http://bitbucket.org/downtown1/left4downtown/"
#endif

Versioning_CheckDependencies(bool:throw=false)
{
	RequireL4D2();
	
#if !SRS_DEBUG

	if(!IsSourcemodVersionValid())
	{
		decl String:version[64];
		GetConVarString(FindConVar("sourcemod_version"), version, sizeof(version));
		
//Fix warnings about evaluating constant expressions
#if SRS_VERSION_REQUIRED_SOURCEMOD_NONDEV
#define ADD_STABLE_STRING " stable"
#else
#define ADD_STABLE_STRING ""
#endif		
		PrintToChatAll("[SRSMOD] Your SourceMod version (%s) is out of date, please upgrade to %s%s", version, SRS_VERSION_REQUIRED_SOURCEMOD, ADD_STABLE_STRING);
		
		if(throw)
		{
			ThrowError("Your SourceMod version (%s) is out of date, please upgrade to %s%s", version, SRS_VERSION_REQUIRED_SOURCEMOD, ADD_STABLE_STRING);
		}
	
#undef ADD_STABLE_STRING
	}
	if(!IsLeft4DowntownVersionValid())
	{
		decl String:version[64];
		new Handle:versionCvar = FindConVar("left4downtown_version");
		if(versionCvar == INVALID_HANDLE)
		{
			strcopy(version, sizeof(version), "0.1.0"); //0.1.0 didn't have a version cvar
		}
		else
		{
			GetConVarString(versionCvar, version, sizeof(version));
		}
		
		PrintToChatAll("[SRSMOD] Your Left4Downtown Extension (%s) is out of date, please upgrade to %s or later", version, SRS_VERSION_REQUIRED_LEFT4DOWNTOWN);
		if(throw)
		{
			ThrowError("Your Left4Downtown Extension (%s) is out of date, please upgrade to %s or later", version, SRS_VERSION_REQUIRED_LEFT4DOWNTOWN);
		}
		return;
	}
#else
//suppress warnings
	if(throw && !throw)
	{
		IsSourcemodVersionValid();
		IsLeft4DowntownVersionValid();
	}
#endif
}

static bool:IsSourcemodVersionValid()
{
	decl String:version[64];
	GetConVarString(FindConVar("sourcemod_version"), version, sizeof(version));
	
	new minVersion = ParseVersionNumber(SRS_VERSION_REQUIRED_SOURCEMOD);
	new versionNumber = ParseVersionNumber(version);

	Debug_Print("SourceMod Minimum version=%x, current=%s (%x)", minVersion, version, versionNumber);
	
	if(versionNumber == minVersion)
	{
#if SRS_VERSION_REQUIRED_SOURCEMOD_NONDEV
		if(StrContains(version, "-dev", false) != -1)
		{
			//1.3.0 stable might not have the latest goods if we ever use the Address functionality in 1.4.0
			return false;
		}
#endif
		return true;
	}
	//newer version or 1.4, assume they know what they're doing
	else if(versionNumber > minVersion)
	{
		return true;
	}
	else
	{
		return false;
	}
}

static bool:IsLeft4DowntownVersionValid()
{
	new Handle:versionCvar = FindConVar("left4downtown_version");
	if(versionCvar == INVALID_HANDLE)
	{
		Debug_Print("Could not find left4downtown_version, maybe using 0.1.0");
		return false;
	}
	
	decl String:version[64];
	GetConVarString(versionCvar, version, sizeof(version));
	
	new minVersion = ParseVersionNumber(SRS_VERSION_REQUIRED_LEFT4DOWNTOWN);
	new versionNumber = ParseVersionNumber(version);

	Debug_Print("Left4Downtown min version=%x, current=%s (%x)", minVersion, version, versionNumber);

	return versionNumber >= minVersion;
}

/* parse a version string such as "1.2.3.4", up to 4 subversions allowed */
static ParseVersionNumber(const String:versionText[])
{
	new String:versionNumbers[4][4];
	ExplodeString(versionText, /* split */ ".", versionNumbers, .maxStrings = 4, .maxStringLength = 4);
	
	new version = 0;
	new shift = 24;
	for(new i = 0; i < sizeof(versionNumbers); i++)
	{
		version = version | (StringToInt(versionNumbers[i]) << shift);
		
		shift -= 8;
	}
	
	//Debug_Print("Parsed version '%s' as %x", versionText, version);
	return version;
}

static RequireL4D2()
{
	decl String:gameName[64];
	GetGameFolderName(gameName, sizeof(gameName));
	if (!StrEqual(gameName, "left4dead2", .caseSensitive = false))
	{
		SetFailState("Plugin supports Left 4 Dead 2 only.");
	}
}